package com.nulldev.util.web.flexibleKit.general;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.data.VariableCaches.VariableCache;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.BaseJSEnvironment;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.evaler.BaseJSEvaluator;
import com.nulldev.util.web.URLUtils.URLUtils;
import com.nulldev.util.web.flexibleKit.flexRenderer.FlexEnvironment;
import com.nulldev.util.web.flexibleKit.flexRenderer.paths.RenderPathGenerator;
import com.nulldev.util.web.flexibleKit.flexRenderer.styling.css.CSSParser;
import com.nulldev.util.web.flexibleKit.flexRenderer.styling.css.StyleSheet;
import com.nulldev.util.web.flexibleKit.flexScript.natives.NativeWindowObject;
import com.nulldev.util.web.flexibleKit.jfvParse.Document;
import com.nulldev.util.web.flexibleKit.jfvParse.Element;
import com.nulldev.util.web.flexibleKit.jfvParse.Node;

public class flexDocument implements AutoCloseable {

	public static enum DocumentStage {
		BLANK,
		GENERATING_RENDER_PATH,
		HANDLING_JS,
		HANDLING_CSS,
		COMPLETE
	}

	public static enum DocumentFlags {
		DISABLE_CSS_PARSER,
		DISABLE_JS_PARSER,
	}

	private final Document doc;
	private volatile List<RenderOperation> cachedOperations;
	private volatile BaseJSEnvironment jsEnvironment;
	private final VariableCache imageCache = new VariableCache(24 * 1024 * 1024); // 24 MB image cache
	private final AtomicBoolean isParsing = new AtomicBoolean();
	private final FlexEnvironment env;
	private final AtomicReference<DocumentStage> stage = new AtomicReference<DocumentStage>(DocumentStage.BLANK);
	private final DocumentFlags[] flags;

	public flexDocument(final Document doc, final DocumentFlags... flags) {
		this.doc = doc;
		this.env = new FlexEnvironment(null, this.imageCache);
		this.flags = flags;
	}

	public flexDocument(final String url, final Document doc, final DocumentFlags... flags) {
		this.doc = doc;
		this.env = new FlexEnvironment(URLUtils.getDomain(url), this.imageCache);
		this.flags = flags;
	}

	public void init() {
		this.isParsing.set(true);
		ExecutorManager.get().await(ExecutorManager.globalAsyncSubmit(() -> {
			stage.lazySet(DocumentStage.GENERATING_RENDER_PATH);
			flexDocument.this.renderPath();
			return null;
		}), ExecutorManager.globalAsyncSubmit(() -> {
			if (ArrayUtils.contains(this.flags, DocumentFlags.DISABLE_JS_PARSER))
				return null;
			stage.lazySet(DocumentStage.HANDLING_JS);
			flexDocument.this.getJSEnvironment();
			return null;
		}));
		stage.lazySet(DocumentStage.COMPLETE);
		this.isParsing.set(false);
	}

	public void destroy() {
		this.doc.clear();
	}

	public Document getFVDocument() {
		return this.doc;
	}

	public BaseJSEnvironment getJSEnvironment() {
		if (this.jsEnvironment == null) {
			this.isParsing.set(true);
			final BaseJSEnvironment e = new BaseJSEnvironment(true, new NativeWindowObject());
			for (final Element el : this.doc.getElementsByTagName("script")) {
				BaseJSEvaluator.eval(e, el.getInnerHTML());
			}
			this.isParsing.lazySet(false);
			return this.jsEnvironment = e;
		} else {
			return this.jsEnvironment;
		}
	}

	public List<RenderOperation> renderPath() {
		if (this.cachedOperations == null) {
			this.isParsing.set(true);
			this.cachedOperations = RenderPathGenerator.make(this);
			this.isParsing.lazySet(false);
			return this.cachedOperations;
		} else {
			return this.cachedOperations;
		}
	}

	public FlexEnvironment getEnvironment() {
		return this.env;
	}

	public void getStyleSheets() {
		final CSSParser cssp = new CSSParser();
		for (final Node n : this.doc.getElementsByTagName("style")) {
			final StyleSheet s = cssp.parse(n.getTextContent());
			JVM.println(s.rules);
		}
	}

	public DocumentStage getStage() {
		return this.stage.get();
	}

	public boolean isParsing() {
		return isParsing.get();
	}

	@Override
	public void close() throws Exception {
		this.imageCache.close();
	}
}
